//! interrupts and exceptions while in supervisor mode come here.
//!
//! the current stack is a kernel stack.
//! push registers, call kerneltrap().
//! when kerneltrap() returns, restore registers, return.

const std = @import("std");
const os = @import("../os.zig");
const print = std.fmt.comptimePrint;

pub fn kernelvec() align(4) callconv(.naked) noreturn {
    // caller-saved registers
    const registers: []const @EnumLiteral() = &.{
        .ra, .gp, .t0, .t1, .t2, .t3, .t4, .t5, .t6,
        .a0, .a1, .a2, .a3, .a4, .a5, .a6, .a7,
    };

    {
        // make room to save registers.
        asm volatile (print("addi sp, sp, -{d}", .{@sizeOf(usize) * registers.len}));
        defer asm volatile (print("addi sp, sp, {d}", .{@sizeOf(usize) * registers.len}));

        // save caller-saved registers.
        inline for (registers, 0..) |register, offset| {
            os.arch.op(.save, register, offset * @sizeOf(usize), .sp);
        }
        // restore registers.
        defer inline for (registers, 0..) |register, offset| {
            os.arch.op(.load, register, offset * @sizeOf(usize), .sp);
        };

        // call the trap handler in trap.zig
        // sp is a callee-saved register, so when kerneltrap returns, sp is naturally restored.
        asm volatile ("call kerneltrap");
    }

    // return to whatever we were doing in the kernel.
    asm volatile ("sret");
}

test "trap-vector alignment" {
    try std.testing.expect(std.mem.isAligned(@intFromPtr(&kernelvec), 4));
}
